package ControllerLayer.ControlServices.Segmenter;

import DataLayer.ImageDataContainer.ImageComponentsInfo.IImageComponentsInfo;
import DataLayer.ImageDataContainer.ImageInfo.IImageInfo;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.util.ArrayList;



public class WordSegmenter implements ISegmentUpdater{
    private BufferedImage img;
    private int[] hist;
    private ArrayList<Integer> localMins;
    private ArrayList<Point>   localMinsPoint;
    private ArrayList<ArrayList<Point>> wordsAll;

    public WordSegmenter(){
            // TODO add implementation
    }

    /**
     * segmentates the given image to words
     * @param imgCompInfo file containing the line information
     * @param image image to segmentate
     */
    public void segmentWords(IImageComponentsInfo imgCompInfo, IImageInfo imageInfo){
        img = imageInfo.getBWImage();
        wordsAll = new ArrayList<ArrayList<Point>>();

        ArrayList<Point> lines = imgCompInfo.getLines();

        for(Point p : lines){
            ////System.out.println(this.getClass() + "szegmentalom ezt a sort: " + p.toString());
            segmentWord(p);
        }
    }


    /**
     * updates the data structure containing the image segmentation info with the
     * word segmentation information
     * @param imgCompInfo data structure containing segmenation info
     */
    public void updateImageCompInfo(IImageComponentsInfo imgCompInfo){
            imgCompInfo.addWords(wordsAll);
    }


    /**
     * segmentates the given line p to words
     * @param p
     */
    private void segmentWord(Point p){
        makeHistogram(p);
        localMinsPoint = new ArrayList<Point>();
        findMin(1);
        makePoints();
        wordsAll.add(localMinsPoint);
        ////System.out.println("sorban levo szavak szama: " + localMinsPoint.size());
    }


     /**
     * makes the column-based histogram of the line with the coordinated given
     * in p
     * @param p the coordinates of the line to segmentate
     */
    private void makeHistogram(Point p){
        int height = img.getWidth();
      //  System.out.println("height: "+height);
        int width = p.y - p.x - 1;
      //  System.out.println("width: "+width);
        hist = new int[height];

        // counts the black pixels in each row
        for (int i = 0; i<height; ++i){
            int sum = 0;
            for(int j = p.x+1; j<p.y; ++j){
                int c = img.getRGB(i, j);
                // condition based on which we count the pixels
                if(c != -1)
                    ++sum;
//                int  red = (c & 0x00ff0000) >> 16;
//                int  green = (c & 0x0000ff00) >> 8;
//                int  blue = c & 0x000000ff;

             //   System.out.println(i+","+j+": "+c);
             //   System.out.println(i+","+j+": "+red+" "+green+" "+blue);
            }
            hist[i] = sum;
        }

       // for (int i = 0; i<height; ++i)
        //    System.out.println(i+": "+hist[i]);

    }

     /**
     * finds the local minimums in the histogram, having their value less than
     * epsilon
     * it only works properly if the words are clearly separated and there is
     * no writing in the first and last pixel column of the picture
     * @param epsilon
     * @return array with the minimum points
     */
    private void findMin(int epsilon){

        int i = 0;
        int j = 1;

        int n = hist.length;
       // System.out.println("hossz: "+n);

        localMins = new ArrayList<Integer>();

        while(j<n){  // we check each row

            boolean found = false;

            // we search for the start of a row: __/
            while((j<n)&&(hist[i]==hist[j])){
                ++i;
                ++j;
                found = true;
            }

            if(found)
                localMins.add(i);

            // find a local maximum:
            while((j<n)&&(hist[i]<=hist[j])){
                ++i;
                ++j;
            }

            // find a local minimum:
            while((j<n)&&(hist[i]>hist[j])){
                ++i;
                ++j;
            }

            localMins.add(i);
        }


        ArrayList<Integer> temp = new ArrayList<Integer>();

        // a temporaly copy of the minimum points having their value less
        // than epsilon

        temp.add(localMins.get(0));  // this is needed if the writing starts exactly
                                     //   at the beginning of the row

        for(int row:localMins)
            if(hist[row]<epsilon)
                temp.add(row);
        
        temp.add(localMins.get(localMins.size()-1));  // this is needed if the writing ends exactly
                                                      //   at the end of the row

        // copy the final result back to localMins
        localMins.clear();
        for(int row:temp)
            localMins.add(row);

//        for(int row:localMins)
//                System.out.println(row);

    }

    /**
     * calculates the sum of the image histogram on the given interval
     * @param a beginning of the interval
     * @param b end of the interval
     * @return sum of the histogram
     */
    private int histSum(int a, int b){
        int sum = 0;
        for(int i = a; i < b; ++i)
            sum = sum + hist[i];
        return sum;
    }

     /**
     * makes an array list of point out of the array list of integers - if the
     * word is wide enough and has enough black pixels
     */
    private void makePoints(){
        localMinsPoint.clear();
        
        int n = localMins.size();
        int i = 0;
        while(i<n-1){

            // checking if the character is wide enough and is not a blank
            // character
            if( (localMins.get(i+1) - localMins.get(i) > 5) && (histSum(localMins.get(i), localMins.get(i+1)) > 5) ){
                Point p = new Point(localMins.get(i), localMins.get(i+1));
                localMinsPoint.add(p);
            }
            ++i;
        }
    }

}
